/* -*-C-*-
 ##############################################################################
 #
 # File:        trice/src/zoom.c
 # RCS:         "@(#)$Revision: 1.18 $ $Date: 94/03/09 11:19:37 $"
 # Description: Routines for configuring zoom parameters on a E1430 module.
 # Author:      Doug Passey
 # Created:     
 # Language:    C
 # Package:     E1430
 # Status:      "@(#)$State: Exp $"
 #
 # (C) Copyright 1992, Hewlett-Packard Company, all rights reserved.
 #
 ##############################################################################
 #
 # Please add additional comments here
 #
 # Revisions:
 #
 ##############################################################################
*/

#    include <stdio.h>
#    include <math.h>

#include "trice.h"
#include "err1430.h"

#ifndef lint
const char i1430_zoom_fileId[] = "$Header: zoom.c,v 1.18 94/03/09 11:19:37 chriss Exp $";
#endif


/*****************************************************************************
 * Set the sample frequency of a module at logical address <la>, to <freq>.
 * Returns 0 if all ok, else return negative error number.
 ****************************************************************************/
SHORTSIZ16 e1430_set_sample_clock_freq_la(SHORTSIZ16 la, FLOATSIZ64 freq)
{
  SHORTSIZ16 error, index;

  error = i1430_get_index_from_la(la, &index);
  if(error) return(error);

  e1430_modStates[index].sampleFreq = (FLOATSIZ32)freq;

  return(0);
}


/*****************************************************************************
 *
 * Returns the sample clock frequency of the E1430 at logical address, 
 * <la>, into the variable pointed to by <freqPtr>.  
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_sample_clock_freq_la(SHORTSIZ16 la, FLOATSIZ64 *freqPtr)
{ 
  SHORTSIZ16 error, reg, index;

  error = e1430_read_register_image(la, E1430_TIMING_SETUP_REG, &reg);
  if(error) return(error);

  /* if external sample clock or VXI backplane and not master */
  if(((reg & ~TIMING_SETUP_ADC_CLOCK_MASK) == TIMING_SETUP_ADC_CLOCK_EXTERNAL)
    || (((reg & ~TIMING_SETUP_MULTI_SYNC_MASK) == TIMING_SETUP_MULTI_SYNC_ON) 
      && ((reg & ~TIMING_SETUP_MASTER_MASK) == TIMING_SETUP_MASTER_OFF))){

    error = i1430_get_index_from_la(la, &index);
    if(error) return(error);

    *freqPtr = e1430_modStates[index].sampleFreq;

  }else{		/* internal sampling clock */
    error = e1430_get_status(la, &reg);
    if(error) return(error);

    if(reg & E1430_MEAS_STATUS_CLOCK_MASK) {
      *freqPtr = 10240000.0;
    }else{
      *freqPtr = 10000000.0;
    }
  }
    
  return (0);
}
 
/*****************************************************************************
 *
 * Set the center frequency, span and zoom mode of a group of E1430s.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_span_zoom(SHORTSIZ16 groupID, FLOATSIZ64 centerFreq, 
				FLOATSIZ64 span, SHORTSIZ16 zoom)
{ 
  SHORTSIZ16 error, deciLevel, deciExtra, dataType;
  FLOATSIZ64 freq, fracFreq, temp, refFreq;
  aModGroupPtr ptr, firstPtr;

  if((firstPtr = i1430_valid_module_group(groupID)) == NULL) {
    return (ERR1430_NO_GROUP);
  }

  error = e1430_get_sample_clock_freq_la(e1430_modStates[*firstPtr].logicalAddr, 
						&refFreq);
  if(error) return(error);

  ptr = firstPtr;
  ptr++;


  for(; *ptr != -1; ptr++) {	/* check all modules for same sample clock */
    error = e1430_get_sample_clock_freq_la(e1430_modStates[*ptr].logicalAddr, &temp);
    if(error) return(error);

    if(temp != refFreq) {
      return(i1430_Error(ERR1430_SAMPLE_CLOCK_FREQ, NULL, NULL));
    }
  }

  if(zoom == E1430_ZOOM_OFF) {
    freq = refFreq/2.56;	/* get alias protected top span for baseband */
    dataType = E1430_DATA_TYPE_REAL;
    fracFreq = 0.0;		
  }else{
    freq = refFreq/1.28;	/* get alias protected top span for zoom */
    fracFreq = centerFreq/refFreq;
    dataType = E1430_DATA_TYPE_COMPLEX;
  }

  freq *= 1.0000001; 	/* fudge a little to get compare to work */

  deciLevel = 0;
  while(1) {
    freq /= 2.0;
    if(freq <= span) break;
    deciLevel++;
  }

  error =e1430_set_center_frequency(groupID, fracFreq);
  if(error) return(error);

  error =e1430_set_data_type(groupID, dataType);
  if(error) return(error);

  deciExtra = deciLevel ? E1430_DECIMATION_ON : E1430_DECIMATION_OFF;

  error = e1430_set_decimation_state(groupID, deciExtra);
  if(error) return(error);

  error = e1430_set_decimation_bandwidth(groupID, deciLevel, deciLevel);
  if(error) return(error);
  
  return(0);

}


/*****************************************************************************
 *
 * Get the span of a group of E1430s.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_span(SHORTSIZ16 groupID, FLOATSIZ64 *spanPtr)
{
  SHORTSIZ16 error, level, deciLevel, deciState, dataType;
  FLOATSIZ64 freq, refFreq;
  aModGroupPtr ptr, firstPtr;
  

  error =e1430_get_data_type(groupID, &dataType);
  if(error) return(error);

  error =e1430_get_decimation_bandwidth(groupID, &deciLevel);
  if(error) return(error);

  error =e1430_get_decimation_state(groupID, &deciState);
  if(error) return(error);

  if((firstPtr = i1430_valid_module_group(groupID)) == NULL) {
    return (ERR1430_NO_GROUP);
  }

  error = e1430_get_sample_clock_freq_la(e1430_modStates[*firstPtr].logicalAddr, 
						&refFreq);
  if(error) return(error);

  ptr = firstPtr;
  ptr++;


  for(; *ptr != -1; ptr++) {	/* check all modules for same sample clock */
    error = e1430_get_sample_clock_freq_la(e1430_modStates[*ptr].logicalAddr, 
								&freq);
    if(error) return(error);

    if(freq != refFreq) {
      return(i1430_Error(ERR1430_SAMPLE_CLOCK_FREQ, NULL, NULL));
    }
  }

  if(dataType == E1430_DATA_TYPE_REAL) {
    freq = refFreq/2.56;	/* alias protected top span for baseband */
  }else{
    freq = refFreq/1.28;	/* alias protected top span for zoom */
  }

  level = (deciState == E1430_DECIMATION_ON) ? 1 : 0;
  if(deciLevel != 0) {
    level += deciLevel - 1;
  }

  while(level) {		/* get span */
    freq /= 2.0;
    level--;
  }

  *spanPtr = freq;

  return(0);
}


/*****************************************************************************
 * Loads <phase> into the ZOOM temporary registers of a group of modules,
 * <groupID>.
 * Returns 0 if all ok, else return negative error number.
 ****************************************************************************/
static SHORTSIZ16 freq_load(SHORTSIZ16 groupID, FLOATSIZ64 freq, 
							SHORTSIZ16 flag)
{
  FLOATSIZ64 modPhase, temp;
  LONGSIZ32 M;
  SHORTSIZ16 N;
  SHORTSIZ16 reg;
  SHORTSIZ16 error;
  SHORTSIZ16 zoomControl;
  SHORTSIZ16 firstLa;
  aModGroupPtr firstPtr, ptr;
  SHORTSIZ16 numE1430s;

  if((firstPtr = i1430_valid_module_group(groupID)) == NULL) {/* no such group */
    return (ERR1430_NO_GROUP);
  }

  firstLa = e1430_modStates[*firstPtr].logicalAddr;

  if(freq == 0.0) {
    zoomControl = 1;	/* zero phase when going to baseband */
  }else{
    zoomControl = 14;	/* otherwise load only frequency */
  }

  numE1430s = i1430_number_of_modules(groupID);	/* how many in group */

  if(numE1430s > 1) {
    /* pull sync to put entire group into a known state in measurement loop */
    error = i1430_pull_sync(firstLa, MEAS_CONTROL_IDLE_MODE);
    if(error) return(error);
  }

  error = i1430_update_group_module_bits(groupID, E1430_ZOOM_CONTROL_REG, 
				ZOOM_CONTROL_OPCODE_MASK, zoomControl);
  if(error) return (error);

  if(flag == 1) {		/* freq is 40 bit number */
    modPhase = freq/500000000.0;
    N = 32 * (SHORTSIZ16)modPhase;

    temp = fmod(freq, 500000000.0);
    M = 8 * (LONGSIZ32)temp;
  }else{
    if(freq < 0.0) {		/* don't use fabs ... download driver barfs */
      modPhase = -freq;
    }else{
      modPhase = freq;
    }
    modPhase = modPhase - floor(modPhase);	/* freq MOD 1 */
  
    temp = modPhase * 2048.0;
    N = (SHORTSIZ16)temp;
    M = 8 * (LONGSIZ32)((temp - (FLOATSIZ64)N) * 500000000.0);
    N *= 32;
  }

  reg = N >> 8;				 /* upper byte of N */
  error = e1430_write_register(groupID, E1430_ZOOM_PHASE_1_REG, reg);
  if(error) return(error);

  reg = N & 0x0FF;			/* lower byte of N */
  error = e1430_write_register(groupID, E1430_ZOOM_PHASE_0_REG, reg);
  if(error) return(error);

  reg = (SHORTSIZ16)(M >> 24);		 /* upper byte of M */
  error = e1430_write_register(groupID, E1430_ZOOM_INTERP_3_REG, reg);
  if(error) return(error);

  reg = (SHORTSIZ16)((M >> 16) & 0x0FF); /* second byte of M */
  error = e1430_write_register(groupID, E1430_ZOOM_INTERP_2_REG, reg);
  if(error) return(error);

  reg = (SHORTSIZ16)((M >> 8) & 0x0FF); /* third byte of M */
  error = e1430_write_register(groupID, E1430_ZOOM_INTERP_1_REG, reg);
  if(error) return(error);

  reg = (SHORTSIZ16)(M & 0x0FF); 	/* lowest byte of M */
  error = e1430_write_register(groupID, E1430_ZOOM_INTERP_0_REG, reg);
  if(error) return(error);

  if(numE1430s == 1) {   /* if only one module, load frequency on the fly */
    error = e1430_write_register_card(firstLa, E1430_LO_TRANSFER_REG, 0);
    if(error) return(error);
  }else{		/* multi module requires yanking the SYNC line */
    error = i1430_pull_sync(firstLa, MEAS_CONTROL_REG_LOAD_MODE);
    if(error) return(error);

    ptr = firstPtr;
    ptr++;

    for(; *ptr != -1; ptr++) {	/* this is a group of modules */
      /* get ready to load register */
      error = e1430_write_register_image(e1430_modStates[*ptr].logicalAddr, 
			E1430_MEAS_CONTROL_REG, 
			MEAS_CONTROL_SYNC_OFF |	MEAS_CONTROL_REG_LOAD_MODE );
      if(error) return (error);
    }

    error = i1430_release_sync(firstLa, MEAS_CONTROL_REG_LOAD_MODE); 
    if(error) return(error);

    error = i1430_pull_sync(firstLa, MEAS_CONTROL_REG_LOAD_MODE);
    if(error) return(error);

    error = i1430_release_sync(firstLa, MEAS_CONTROL_REG_LOAD_MODE); 
    if(error) return(error);
  }

  zoomControl = 7;	/* restore to capture phase mode */
  error = i1430_update_group_module_bits(groupID, E1430_ZOOM_CONTROL_REG, 
				ZOOM_CONTROL_OPCODE_MASK, zoomControl);
  if(error) return (error);

  return (0);
}


/*****************************************************************************
 *
 * Set center frequency of module group, <groupID>, to <frequency>, where
 * <frequency> is in the range of 0.0 -> 1.0 Fs.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_center_frequency(SHORTSIZ16 groupID, FLOATSIZ64 frequency)
{
  return (freq_load(groupID, frequency, 0));
}


/*****************************************************************************
 *
 * Set center frequency of module group, <groupID>, to <frequency>, where
 * <frequency> holds the combination of the phase and interp info.  This
 * is a special function for STEALTH.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_set_center_freq_bits(SHORTSIZ16 groupID, FLOATSIZ64 frequency)
{
  return (freq_load(groupID, frequency, 1));
}


/*****************************************************************************
 *
 * Return center frequency of module at <index> in e1430_modStates array.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
FLOATSIZ64 i1430_get_center_freq_index(SHORTSIZ16 index)
{ 
  SHORTSIZ16 phase;
  LONGSIZ32 interp;

  phase = ((e1430_modStates[index].zoomPhase0 & 0x0E0) >> 5) | 
	  ((e1430_modStates[index].zoomPhase1 & 0xFF) << 3);

  interp = (((LONGSIZ32)e1430_modStates[index].zoomInterp0 & 0x0F8L) >> 3) | 
  	   (((LONGSIZ32)e1430_modStates[index].zoomInterp1 & 0x0FFL) << 5) | 
  	   (((LONGSIZ32)e1430_modStates[index].zoomInterp2 & 0x0FFL) << 13) | 
  	   (((LONGSIZ32)e1430_modStates[index].zoomInterp3 & 0x0FFL) << 21);

  return (((FLOATSIZ64)phase + ((FLOATSIZ64)interp/500000000.0))/2048.0);
}


/*****************************************************************************
 *
 * Returns the center frequency of the module group, <groupID>, into
 * the variable pointed to by <freqPtr>.  
 * Returns 0 if OK, returns ERR1430_PARAMETER_UNEQUAL if the source is
 * not the same for all modules in the group.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_center_frequency(SHORTSIZ16 groupID, FLOATSIZ64 *freqPtr)
{ 
  return (i1430_get_float_parm(groupID, i1430_get_center_freq_index, 
				"center frequency", freqPtr));
}
 

/*****************************************************************************
 *
 * Reset the DSP section.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_reset_dsp(SHORTSIZ16 groupID)
{ 
  SHORTSIZ16 firstLa, error;
  aModGroupPtr firstPtr, ptr;

  if((firstPtr = i1430_valid_module_group(groupID)) == NULL) {/* no such group */
    return (ERR1430_NO_GROUP);
  }

  firstLa = e1430_modStates[*firstPtr].logicalAddr;

  /* reset DSP */
  error = i1430_pull_sync(firstLa, MEAS_CONTROL_RESET_DSP_MODE);
  if(error) return(error);

  ptr = firstPtr;
  ptr++;		/* point to next module in group */

  for(; *ptr != -1; ptr++) {
    error = e1430_write_register_image(e1430_modStates[*ptr].logicalAddr, 
		E1430_MEAS_CONTROL_REG,
		MEAS_CONTROL_RESET_DSP_MODE | MEAS_CONTROL_SYNC_OFF);
    if(error) return(error);
  }

  error = i1430_release_sync(firstLa, MEAS_CONTROL_RESET_DSP_MODE); 
  if(error) return(error);

  error = i1430_pull_sync(firstLa, MEAS_CONTROL_RESET_DSP_MODE);
  if(error) return(error);

  error = i1430_release_sync(firstLa, MEAS_CONTROL_RESET_DSP_MODE); 

  /* put slaves back into idle before pulling sync again */
  ptr = firstPtr;
  ptr++;		/* point to next module in group */

  for(; *ptr != -1; ptr++) {
    error = e1430_write_register_image(e1430_modStates[*ptr].logicalAddr, 
		E1430_MEAS_CONTROL_REG,
		MEAS_CONTROL_IDLE_MODE | MEAS_CONTROL_SYNC_OFF);
    if(error) return(error);
  }

  
  /* now zero the LO phase */
  error = i1430_pull_sync(firstLa, MEAS_CONTROL_IDLE_MODE); 

  /* set zoom control to load phase */
  error = i1430_update_group_module_bits(groupID, E1430_ZOOM_CONTROL_REG, 
				ZOOM_CONTROL_OPCODE_MASK, 15);
  if(error) return (error);


  error = e1430_write_register(groupID, E1430_ZOOM_PHASE_1_REG, 0);
  if(error) return(error);

  error = e1430_write_register(groupID, E1430_ZOOM_PHASE_0_REG, 0);
  if(error) return(error);

  error = e1430_write_register(groupID, E1430_ZOOM_INTERP_3_REG, 0);
  if(error) return(error);

  error = e1430_write_register(groupID, E1430_ZOOM_INTERP_2_REG, 0);
  if(error) return(error);

  error = e1430_write_register(groupID, E1430_ZOOM_INTERP_1_REG, 0);
  if(error) return(error);

  error = e1430_write_register(groupID, E1430_ZOOM_INTERP_0_REG, 0);
  if(error) return(error);
 
  error = i1430_pull_sync(firstLa, MEAS_CONTROL_REG_LOAD_MODE);
  if(error) return(error);

  ptr = firstPtr;
  ptr++;

  for(; *ptr != -1; ptr++) {	/* this is a group of modules */
    /* get ready to load register */
    error = e1430_write_register_image(e1430_modStates[*ptr].logicalAddr, 
			E1430_MEAS_CONTROL_REG, 
			MEAS_CONTROL_SYNC_OFF |	MEAS_CONTROL_REG_LOAD_MODE );
    if(error) return (error);
  }

  error = i1430_release_sync(firstLa, MEAS_CONTROL_REG_LOAD_MODE); 
  if(error) return(error);

  error = i1430_pull_sync(firstLa, MEAS_CONTROL_REG_LOAD_MODE);
  if(error) return(error);

  error = i1430_release_sync(firstLa, MEAS_CONTROL_REG_LOAD_MODE); 
  if(error) return(error);

  /* put zoom control back into mode to measure phase on SYNC transitions */
  error = i1430_update_group_module_bits(groupID, E1430_ZOOM_CONTROL_REG, 
				ZOOM_CONTROL_OPCODE_MASK, 7);

  return(error);
}
 

